﻿using System;
using System.Reflection;

namespace UpdateSoon
{
    public class SharpZipLibUtils
    {
        public const string SharpZipLib_DLLName = "Dlls/ICSharpCode.SharpZipLib.dll";
        public const string TypeName_ZipInputStream = "ICSharpCode.SharpZipLib.Zip.ZipInputStream";
        public const string TypeName_ZipEntry = "ICSharpCode.SharpZipLib.Zip.ZipEntry";

        static Assembly mAssembly_SharpZipLib { get; set; }

        /// <summary>
        /// <para>解压zip文件</para>
        /// <para>加载在程序资源的 ICSharpCode.SharpZipLib.dll，并且使用反射方式执行 Unzip 逻辑</para>
        /// </summary>
        /// <param name="zipFilePath"></param>
        /// <param name="unzipDirectoryPath"></param>
        /// <exception cref="Exception"></exception>
        public static void Unzip(string zipFilePath, string unzipDirectoryPath)
        {
            try
            {
                if (mAssembly_SharpZipLib == null)
                {
                    #region 从资源中找到 ICSharpCode.SharpZipLib.dll, 并且动态加载

                    var rsi = System.Windows.Application.GetResourceStream(new Uri(SharpZipLib_DLLName, UriKind.Relative));

                    using (System.IO.Stream stream = rsi.Stream)
                    {
                        if (stream == null) { return; }

                        byte[] assemblyRawBytes = new byte[stream.Length];
                        stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length); // 读取dll流
                        mAssembly_SharpZipLib = Assembly.Load(assemblyRawBytes); // 动态加载 ICSharpCode.SharpZipLib.dll
                    }

                    #endregion
                }

                if (mAssembly_SharpZipLib == null) // 复查
                {
                    throw new Exception($"程序资源找不到{SharpZipLib_DLLName}");
                }

                Type typeOfZipInputStream = mAssembly_SharpZipLib.GetType(TypeName_ZipInputStream);

                // 构造函数参数
                System.IO.FileStream argsFileStream = System.IO.File.Open(zipFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);

                object zipInputStream = mAssembly_SharpZipLib.CreateInstance
                (
                    typeName: TypeName_ZipInputStream,
                    ignoreCase: false,
                    bindingAttr: BindingFlags.CreateInstance, // 重点, 卡在这里了
                    binder: null,
                    args: new object[1] { argsFileStream },
                    culture: System.Globalization.CultureInfo.CurrentCulture,
                    activationAttributes: null
                );

                Type typeOfZipEntry = mAssembly_SharpZipLib.GetType(TypeName_ZipEntry);

                object zipEntry = typeOfZipInputStream.GetMethod("GetNextEntry").Invoke(zipInputStream, null);

                while (zipEntry != null)
                {
                    string zipEntryName = typeOfZipEntry.GetProperty("Name").GetValue(zipEntry, null) as string;

                    string filePath = System.IO.Path.Combine(unzipDirectoryPath, zipEntryName);
                    var fi = new System.IO.FileInfo(filePath);
                    if (fi.Directory.Exists == false) fi.Directory.Create();
                    using (var fileStreamOut = new System.IO.FileStream(filePath, System.IO.FileMode.Create, System.IO.FileAccess.Write))
                    {
                        int size = 2048;
                        byte[] buffer = new byte[size];
                        while (true)
                        {
                            size = (int)typeOfZipInputStream.GetMethod("Read").Invoke(zipInputStream, new object[3] { buffer, 0, size });

                            if (size > 0)
                            {
                                fileStreamOut.Write(buffer, 0, size);
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                    zipEntry = typeOfZipInputStream.GetMethod("GetNextEntry").Invoke(zipInputStream, null);
                }

                typeOfZipInputStream.GetMethod("Close").Invoke(zipInputStream, null);
            }
            finally
            {
                GC.Collect();
            }
        }
    }
}
